/*
 * kl_lib_f0.cpp
 *
 *  Created on: 10.12.2012
 *      Author: kreyl
 */

#include "kl_lib_L15x.h"
#include <stdarg.h>
#include <string.h>
#include "tiny_sprintf.h"

// ================================ Timer ======================================
void Timer_t::Init(TIM_TypeDef* Tmr) {
    ITmr = Tmr;
    if     (ITmr == TIM2)  { rccEnableTIM2(FALSE); }
    else if(ITmr == TIM3)  { rccEnableTIM3(FALSE); }
    else if(ITmr == TIM4)  { rccEnableTIM4(FALSE); }
    else if(ITmr == TIM6)  { rccEnableAPB1(RCC_APB1ENR_TIM6EN,  FALSE); }
    else if(ITmr == TIM7)  { rccEnableAPB1(RCC_APB1ENR_TIM7EN,  FALSE); }
    else if(ITmr == TIM9)  { rccEnableAPB2(RCC_APB2ENR_TIM9EN,  FALSE); }
    else if(ITmr == TIM10) { rccEnableAPB2(RCC_APB2ENR_TIM10EN, FALSE); }
    else if(ITmr == TIM11) { rccEnableAPB2(RCC_APB2ENR_TIM11EN, FALSE); }
    // Clock src
    if(ANY_OF_3(ITmr, TIM9, TIM10, TIM11)) PClk = &Clk.APB2FreqHz;
    else PClk = &Clk.APB1FreqHz;
}

void Timer_t::PwmInit(GPIO_TypeDef *GPIO, uint16_t N, uint8_t Chnl, Inverted_t Inverted, const PinSpeed_t ASpeed) {
    // GPIO
    if              (ITmr == TIM2)              PinSetupAlterFunc(GPIO, N, omPushPull, pudNone, AF1, ASpeed);
    else if(ANY_OF_2(ITmr, TIM3, TIM4))         PinSetupAlterFunc(GPIO, N, omPushPull, pudNone, AF2, ASpeed);
    else if(ANY_OF_3(ITmr, TIM9, TIM10, TIM11)) PinSetupAlterFunc(GPIO, N, omPushPull, pudNone, AF3, ASpeed);
    // Output
    uint16_t tmp = (Inverted == invInverted)? 0b111 : 0b110; // PWM mode 1 or 2
    switch(Chnl) {
        case 1:
            PCCR = &ITmr->CCR1;
            ITmr->CCMR1 |= (tmp << 4);
            ITmr->CCER  |= TIM_CCER_CC1E;
            break;

        case 2:
            PCCR = &ITmr->CCR2;
            ITmr->CCMR1 |= (tmp << 12);
            ITmr->CCER  |= TIM_CCER_CC2E;
            break;

        case 3:
            PCCR = &ITmr->CCR3;
            ITmr->CCMR2 |= (tmp << 4);
            ITmr->CCER  |= TIM_CCER_CC3E;
            break;

        case 4:
            PCCR = &ITmr->CCR4;
            ITmr->CCMR2 |= (tmp << 12);
            ITmr->CCER  |= TIM_CCER_CC4E;
            break;

        default: break;
    }
}



//// ================================ PWM pin ====================================
//void PwmPin_t::Init(GPIO_TypeDef *GPIO, uint16_t N, uint8_t TimN, uint8_t Chnl, uint16_t TopValue, bool Inverted) {
//    PinSetupAlterFuncOutput(GPIO, N, omPushPull);
//    switch(TimN) {
//        case 1:
//            Tim = TIM1;
//            rccEnableTIM1(FALSE);
//            break;
//        case 2:
//            Tim = TIM2;
//            rccEnableTIM2(FALSE);
//            break;
//
//        case 3:
//            Tim = TIM3;
//            rccEnableTIM3(FALSE);
//            break;
//#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL)
//        case 4:
//            Tim = TIM4;
//            rccEnableTIM4(FALSE);
//            break;
//#endif
//        case 15:
//            Tim = TIM15;
//            rccEnableAPB2(RCC_APB2ENR_TIM15EN, FALSE);
//            break;
//        case 16:
//            Tim = TIM16;
//            rccEnableAPB2(RCC_APB2ENR_TIM16EN, FALSE);
//            break;
//
//        case 17:
//            Tim = TIM17;
//            rccEnableAPB2(RCC_APB2ENR_TIM17EN, FALSE);
//            break;
//
//        default: return; break;
//    }
//
//    // Clock src
//    if(ANY_OF_4(TimN, 1, 15, 16, 17)) PClk = &Clk.APB2FreqHz;
//    else PClk = &Clk.APB1FreqHz;
//
//    // Common
//    Tim->CR1 = TIM_CR1_CEN; // Enable timer, set clk division to 0, AutoReload not buffered
//    Tim->CR2 = 0;
//    Tim->ARR = TopValue;
//    Tim->BDTR = TIM_BDTR_MOE | TIM_BDTR_AOE;
//
//    // Output
//    uint16_t tmp = Inverted? 0b111 : 0b110; // PWM mode 1 or 2
//    switch(Chnl) {
//        case 1:
//            PCCR = &Tim->CCR1;
//            Tim->CCMR1 |= (tmp << 4);
//            Tim->CCER  |= TIM_CCER_CC1E;
//            break;
//
//        case 2:
//            PCCR = &Tim->CCR2;
//            Tim->CCMR1 |= (tmp << 12);
//            Tim->CCER  |= TIM_CCER_CC2E;
//            break;
//
//        case 3:
//            PCCR = &Tim->CCR3;
//            Tim->CCMR2 |= (tmp << 4);
//            Tim->CCER  |= TIM_CCER_CC3E;
//            break;
//
//        case 4:
//            PCCR = &Tim->CCR4;
//            Tim->CCMR2 |= (tmp << 12);
//            Tim->CCER  |= TIM_CCER_CC4E;
//            break;
//
//        default: break;
//    }
//    *PCCR = 0;
//}
//
//void PwmPin_t::SetFreqHz(uint32_t FreqHz) {
//    uint32_t divider = Tim->ARR * FreqHz;
//    if(divider == 0) return;
//    uint32_t FPrescaler = *PClk / divider;
//    if(FPrescaler != 0) FPrescaler--;   // do not decrease in case of high freq
//    Tim->PSC = (uint16_t)FPrescaler;
//}
//
// ================================= DEBUG =====================================
//void chDbgPanic(const char *msg) {
//    Uart.PrintNow(msg);
//    (void)msg;
//}

// ============================== UART command =================================
#ifdef DBG_UART_ENABLED
DbgUart_t Uart;

void DbgUart_t::Printf(const char *format, ...) {
    va_list args;
    va_start(args, format);
    uint32_t Cnt = tiny_vsprintf(SprintfBuf, UART_TXBUF_SIZE, format, args);
    va_end(args);

    // Put data to buffer
    uint8_t *p = (uint8_t*)SprintfBuf;
    IFullSlotsCount += Cnt;
    uint32_t PartSz = (TXBuf + UART_TXBUF_SIZE) - PWrite;  // Data from PWrite to right bound
    if(Cnt > PartSz) {
        memcpy(PWrite, p, PartSz);
        PWrite = TXBuf;     // Start from beginning
        p += PartSz;
        Cnt -= PartSz;
    }
    memcpy(PWrite, p, Cnt);
    PWrite += Cnt;
    if(PWrite >= (TXBuf + UART_TXBUF_SIZE)) PWrite = TXBuf; // Circulate pointer

    // Start transmission if Idle
    if(IDmaIsIdle) {
        IDmaIsIdle = false;
        dmaStreamSetMemory0(UART_DMA, PRead);
        PartSz = (TXBuf + UART_TXBUF_SIZE) - PRead;
        ITransSize = (IFullSlotsCount > PartSz)? PartSz : IFullSlotsCount;
        dmaStreamSetTransactionSize(UART_DMA, ITransSize);
        dmaStreamSetMode(UART_DMA, UART_DMA_MODE);
        dmaStreamEnable(UART_DMA);
    }
}

// ==== Init & DMA ====
// Wrapper for IRQ
extern "C" {
void CmdUartTxIrq(void *p, uint32_t flags) { Uart.IRQDmaTxHandler(); }
}
void DbgUart_t::Init(uint32_t ABaudrate) {
    PWrite = TXBuf;
    PRead = TXBuf;
    IFullSlotsCount = 0;
    IDmaIsIdle = true;
    PinSetupAlterFunc(UART_GPIO, UART_TX_PIN, omPushPull, pudNone, UART_AF);

    // ==== USART configuration ====
    UART_RCC_ENABLE();     // UART clock
    UART->CR1 = USART_CR1_UE;     // Enable USART
    if(UART == USART1) UART->BRR = Clk.APB2FreqHz / ABaudrate;
    else               UART->BRR = Clk.APB1FreqHz / ABaudrate;
    UART->CR2 = 0;
    UART->CR3 = USART_CR3_DMAT;   // Enable DMA at transmitter
    UART->CR1 = USART_CR1_TE;     // Transmitter enabled

    // ==== DMA ====
    // Here only the unchanged parameters of the DMA are configured.
    dmaStreamAllocate     (UART_DMA, IRQ_PRIO_MEDIUM, CmdUartTxIrq, NULL);
    dmaStreamSetPeripheral(UART_DMA, &UART->DR);
    dmaStreamSetMode      (UART_DMA, UART_DMA_MODE);

    UART->CR1 |= USART_CR1_UE;        // Enable USART
}


// ==== IRQs ====
void DbgUart_t::IRQDmaTxHandler() {
    dmaStreamDisable(UART_DMA);    // Registers may be changed only when stream is disabled
    IFullSlotsCount -= ITransSize;
    PRead += ITransSize;
    if(PRead >= (TXBuf + UART_TXBUF_SIZE)) PRead = TXBuf; // Circulate pointer

    if(IFullSlotsCount == 0) IDmaIsIdle = true; // Nothing left to send
    else {  // There is something to transmit more
        dmaStreamSetMemory0(UART_DMA, PRead);
        uint32_t PartSz = (TXBuf + UART_TXBUF_SIZE) - PRead;
        ITransSize = (IFullSlotsCount > PartSz)? PartSz : IFullSlotsCount;
        dmaStreamSetTransactionSize(UART_DMA, ITransSize);
        dmaStreamSetMode(UART_DMA, UART_DMA_MODE);
        dmaStreamEnable(UART_DMA);    // Restart DMA
    }
}
#endif

